#include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "charts.h"
#include "helps.h"
#include "list.h"
#include "utils.h"

static List *vars[27];
static Chart chart_info;

void cmd_agm() {
	char *n1 = strtok(NULL, " "), *n2 = strtok(NULL, " "), string[64], *end;
	float num1, num2, a0, g0, a, g;
	int time = 0;
	if (!n1 || !n2) {
		puts(gettext("agm takes 2 number as arguments."));
		return;
	}
	num1 = strtof(n1, &end);
	if ((num1== 0) && (end == n1)) {
		printf(gettext("Invalid number: \"%s\".\n"), n1);
		return;
	}
	num2 = strtof(n2, &end);
	if ((num2 == 0) && (end == n2)) {
		printf(gettext("Invalid number: \"%s\".\n"), n2);
		return;
	}
	a = (num1 + num2) / 2.0; g = sqrt(num1 * num2);
	a0 = a; g0 = g;
	while (fabs(a - g) > 0.00001) {
		a = (a0 + g0) / 2.0; g = sqrt(a0 * g0);
		a0 = a; g0 = g;
		time ++;
		if (time > 256) {
			break;
		}
	}
	display_num(string, num1);
	printf("agm(%s, ", string);
	display_num(string, num2);
	printf("%s) ~= ", string);
	float result = (a0 +g0) / 2.0;
	display_num(string, result);
	printf("%s\n", string);
}

void cmd_append() {
	char *end, *name = strtok(NULL, " "), string[64], *value;
	float num;
	int var = get_var(name);
	if (var < 0) {
		return;
	}
	if (!vars[var]) {
		vars[var] = new_list();
	}
	while ((value = strtok(NULL, " ")) != NULL) {
		num = strtof(value, &end);
		if ((num == 0) && (end == value)) {
			printf(gettext("Invalid number: \"%s\".\n"), value);
		} else {
			if (append_list(vars[var], num) != 0) {
				printf(gettext("Max size of list is %d.\n"), MAX_DATA);
				break;
			}
		}
	}
	if (length_list(vars[var]) <= 5) {
		printf("%c = [", var + 'A');
		for (int i = 0; i <= length_list(vars[var]) - 1; i ++) {
			display_num(string, get_list(vars[var], i));
			if (i == length_list(vars[var]) - 1) {
				printf("%s", string);
			} else {
				printf("%s, ", string);
			}
		}
		printf("]\n");
	} else {
		printf(gettext("%c = <%d elememts list>\n"), var + 'A', length_list(vars[var]));
	}
}

void reset_chart_info() {
	chart_info.type = -1;
	chart_info.draw_dot = 0;
	chart_info.la_type = LA_NONE;
	chart_info.label_x = NULL;
	chart_info.value_x = NULL;
	chart_info.label_y = NULL;
	chart_info.value_y = NULL;
}

void cmd_chart() {
	char *action = strtok(NULL, " ");
	if (strcmp(action, "draw") == 0) {
		if ((!chart_info.type) || (chart_info.type < 0)) {
			puts(gettext("Please input a chart type."));
			return;
		}
		if (!(chart_info.value_x && chart_info.value_y)) {
			puts(gettext("Please input values for chart."));
			return;
		}
		if (length_list(chart_info.value_x) != length_list(chart_info.value_y)) {
			puts(gettext("Values of x and y must be same size."));
			return;
		}
		for (int i = 0; i <= length_list(chart_info.value_x) - 1; i ++) {
			if (get_list(chart_info.value_x, i) < 0) {
				puts(gettext("Values of x-axis must greater than 0."));
			}
		}
		if (init_chart() == -1) {
			return;
		}
		draw_chart(&chart_info);
		mainloop();
	} else if (strcmp(action, "reset") == 0) {
		reset_chart_info();
	} else if (strncmp(action, "set", 3) == 0) {
		char *part = action + 3;
		if (strcmp(part, "x") == 0) {
			char *value = strtok(NULL, " "), *label = strtok(NULL, " ");
			int var = get_var(value);
			if (var < 0) {
				return;
			}
			if (!vars[var]) {
				printf(gettext("Variable \"%c\" isn't initialized.\n"), var + 'A');
				return;
			}
			chart_info.value_x = vars[var];
			if (!label) {
				chart_info.label_x = "x";
			} else {
				chart_info.label_x = label;
			}
		} else if (strcmp(part, "y") == 0) {
			char *value = strtok(NULL, " "), *label = strtok(NULL, " ");
			int var = get_var(value);
			if (var < 0) {
				return;
			}
			if (!vars[var]) {
				printf(gettext("Variable \"%c\" isn't initialized.\n"), var + 'A');
				return;
			}
			chart_info.value_y = vars[var];
			if (!label) {
				chart_info.label_y = "y";
			} else {
				chart_info.label_y = label;
			}
		} else if (strcmp(part, "type") == 0) {
			int i = 0;
			const char *table[] = {"bar", "line", "scatter", NULL};
			char *str = strtok(NULL, " ");
			if (!str) {
				puts(gettext("Please input a chart type."));
				return;
			}
			while (table[i]) {
				if (strcmp(table[i], str) == 0) {
					chart_info.type = i;
					char *option = strtok(NULL, " ");
					if (!option) {
						return;
					}
					switch (chart_info.type) {
						case CHART_LINE:
							if (strcmp(option, "drawdot") == 0) {
								chart_info.draw_dot = 1;
							}
							return;
						case CHART_SCATTER:
							if (strncmp(option, "la=", 3) == 0) {
								if (strcmp(option + 3, "none") == 0) {
									chart_info.la_type = LA_NONE;
								} else if (strcmp(option + 3, "slr") == 0) {
									chart_info.la_type = LA_SLR;
								} else {
									printf(gettext("No regression analysis type: \"%s\".\n"),
											option + 3);
								}
							}
							return;
					}
				}
				i ++;
			}
			printf(gettext("No chart type: \"%s\".\n"), str);
		} else {
			printf(gettext("\"charts set%s\" is an invalid subcommand.\n"), part);
		}
	} else {
		printf(gettext("No subcommand \"%s\" found.\n"), action);
	}
}

void cmd_ghm() {
	char *n1 = strtok(NULL, " "), *n2 = strtok(NULL, " "), string[64], *end;
	float num1, num2, g0, h0, g, h;
	int time = 0;
	if (!n1 || !n2) {
		puts(gettext("ghm takes 2 number as arguments."));
		return;
	}
	num1 = strtof(n1, &end);
	if ((num1== 0) && (end == n1)) {
		printf(gettext("Invalid number: \"%s\".\n"), n1);
		return;
	}
	num2 = strtof(n2, &end);
	if ((num2 == 0) && (end == n2)) {
		printf(gettext("Invalid number: \"%s\".\n"), n2);
		return;
	}
	g = sqrt(num1 * num2); h = 2 / ((1 / num1) + (1 / num2));
	g0 = g; h0 = h;
	while (fabs(g - h) > 0.00001) {
		g = sqrt(g0 * h0); h = 2 / ((1 / g0) + (1 / h0));
		g0 = g; h0 = h;
		time ++;
		if (time > 256) {
			break;
		}
	}
	display_num(string, num1);
	printf("ghm(%s, ", string);
	display_num(string, num2);
	printf("%s) ~= ", string);
	float result = (g0 +h0) / 2.0;
	display_num(string, result);
	printf("%s\n", string);
}

void cmd_load() {
	char *name = strtok(NULL, " "), *end, buf[128];
	int var = -1;
	float num;
	if (!name) {
		puts(gettext("Please input a file name."));
		return;
	}
	FILE *fp = fopen(name, "r");
	if (!fp) {
		perror(name);
		return;
	}
	while (fgets(buf, sizeof(buf), fp)) {
		if (strncmp(buf, "name=", 5) == 0) {
			var = get_var(buf + 5);
			if (var >= 0) {
				if (!vars[var]) {
					vars[var] = new_list();
				}
			}
		} else if (strncmp(buf, "value=", 6) == 0) {
			if (var == -1) {
				continue;
			}
			num = strtof(buf + 6, &end);
			if (end != buf + 6) {
				append_list(vars[var], num);
			}
		}
	}
	fclose(fp);
}

void cmd_save() {
	char *name = strtok(NULL, " "), string[64];
	if (!name) {
		puts(gettext("Please input a file name."));
		return;
	}
	FILE *fp = fopen(name, "w");
	if (!fp) {
		perror(name);
		return;
	}
	for (int var = 0; var <= 25; var ++) {
		if (vars[var]) {
			fprintf(fp, "name=%c\n", var + 'A');
			for (int i = 0; i <= length_list(vars[var]) -1; i ++) {
				fprintf(fp, "value=%f\n", get_list(vars[var], i));
			}
		}
	}
	fclose(fp);
}

void cmd_show() {
	char *name = strtok(NULL, " "), string[64];
	char *c1 = strtok(NULL, " "), *op = strtok(NULL, " "), *c2 = strtok(NULL, " ");
	int var = get_var(name), filter = 0, count = 0, total;
	if (var < 0) {
		return;
	}
	if (!vars[var]) {
		printf(gettext("Variable \"%c\" isn't initialized.\n"), var + 'A');
		return;
	}
	total = length_list(vars[var]);
	if (c1) {
		filter = 1;
		if (is_eligible(0, c1, op, c2) == -1) {
			return;
		}
	}
	if (filter == 1) {
		vars[26]->len = -1;
		for (int i = 0; i <= length_list(vars[var]) - 1; i ++) {
			if (is_eligible(get_list(vars[var], i), c1, op, c2) == 1) {
				append_list(vars[26], get_list(vars[var], i));
				count ++;
			}
		}
	}
	printf("%c = [", var + 'A');
	if (filter == 1) {
		var = 26;
	}
	for (int i = 0; i <= length_list(vars[var]) - 1; i ++) {
		display_num(string, get_list(vars[var], i));
		if (i == length_list(vars[var]) - 1) {
			printf("%s", string);
		} else {
			printf("%s, ", string);
		}
	}
	printf("]\n");
	if (filter == 1) {
		int percent = (1.0 * count / total) * 100;
		display_num(string, percent);
		printf(gettext("Find %d(%d%%) element(s).\n"), count, percent);
	}
	vars[26]->len = -1;
}

void cmd_sort() {
	const char *table[] = {"asc", "desc", NULL};
	char *name = strtok(NULL, " "), *type = strtok(NULL, " ");
	int i = 0, var = get_var(name);
	if (var < 0) {
		return;
	}
	if (!vars[var]) {
		printf(gettext("Variable \"%c\" isn't initialized.\n"), var + 'A');
		return;
	}
	if (!type) {
		puts(gettext("Please select a sort type."));
		return;
	}
	while (table[i]) {
		if (strcmp(type, table[i]) == 0) {
			sort_list(vars[var], i);
			return;
		}
		i ++;
	}
	printf(gettext("No sort type: \"%s\".\n"), type);
}

void cmd_statistic() {
	const char *table[] = {
		"gmean", "hmean", "max", "mean", "min",
		"pstdev", "pvar", "qmean", "sum",
		NULL
	};
	char *name = strtok(NULL, " "), string[64], *type = strtok(NULL, " ");
	int i = 0, var = get_var(name);
	if (var < 0) {
		return;
	}
	if (!vars[var]) {
		printf(gettext("Variable \"%c\" isn't initialized.\n"), var + 'A');
		return;
	}
	if (!type) {
		puts(gettext("Please select a statistic type."));
		return;
	}
	while (table[i]) {
		if (strcmp(type, table[i]) == 0) {
			display_num(string, statistic_list(vars[var], i));
			printf("%s(%c) = %s\n", table[i], var + 'A', string);
			return;
		}
		i ++;
	}
	printf(gettext("No statistic type: \"%s\".\n"), type);
}

void free_vars() {
	for (int i = 0; i <= 26; i ++) {
		if (vars[i]) {
			free(vars[i]);
		}
	}
}

int main(int argc, char *argv[]) {
	setlocale(LC_ALL, "");
	bindtextdomain("counts", "/usr/share/locale");
	textdomain("counts");
	parse_args(argc, argv);
	int major, minor, patch;
	get_version(&major, &minor, &patch);
	if (args_showversion == 1) {
		printf("Counts %d.%d.%d\n", major, minor, patch);
		return EXIT_SUCCESS;
	}
	if (args_showhelp == 1) {
		help_cmdargs(argv[0]);
		return EXIT_SUCCESS;
	}
	if (args_hideinfo != 1) {
		printf("Counts %d.%d.%d (%s, %s, %s)\n", major, minor, patch, get_compiler(), __DATE__, __TIME__);
		puts(gettext("Type \"license\", \"?\" for more information; \"exit\" to exit."));
	}
	atexit(free_vars);
	reset_chart_info();
	vars[26] = new_list();
	char *input;
	while ((input = readline(">>> ")) != NULL) {
		if (strlen(input) > 0) {
			add_history(input);
		}
		if (strncmp(input, "agm", 3) == 0) {
			strtok(input, " ");
			cmd_agm();
		} else if (strncmp(input, "append", 6) == 0) {
			strtok(input, " ");
			cmd_append();
		} else if (strncmp(input, "chart ", 6) == 0) {
			strtok(input, " ");
			cmd_chart();
		} else if ((strcmp(input, "exit") == 0) || (strcmp(input, "quit") == 0)) {
			break;
		} else if (strncmp(input, "ghm", 3) == 0) {
			strtok(input, " ");
			cmd_ghm();
		} else if (strcmp(input, "license") == 0) {
			print_license();
		} else if (strncmp(input, "load ", 5) == 0) {
			strtok(input, " ");
			cmd_load();
		} else if (strncmp(input, "save ", 5) == 0) {
			strtok(input, " ");
			cmd_save();
		} else if (strncmp(input, "show ", 5) == 0) {
			strtok(input, " ");
			cmd_show();
		} else if (strncmp(input, "sort ", 5) == 0) {
			strtok(input, " ");
			cmd_sort();
		} else if (strncmp(input, "statistic", 9) == 0) {
			strtok(input, " ");
			cmd_statistic();
		} else if (input[0] == '?') {
			if (strcmp(input, "?") == 0) {
				puts(gettext("Type \"?[command]\" for help.\n"));
				puts(gettext("Available commands:"));
				puts("  agm     append chart exit ghm  load");
				puts("  license save   show  sort statistic");
				puts("  quit");
			} else {
				print_help(input + 1);
			}
		} else {
			if (strlen(input) > 0) {
				printf(gettext("Can't parse command: \"%s\".\n"), input);
			}
		}
	}
	free(input);
	return EXIT_SUCCESS;
}

